package com.demo.tool;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import fi.iki.elonen.NanoHTTPD;

public class ServerTool {

    public NanoHTTPD.Response respond(  Map<String, String> headers, NanoHTTPD.IHTTPSession session,
                                      String uri , List<File> rootDirs, WkHtmlTool wkHtmlTool) {
        NanoHTTPD.Response r = null;
        try {
            r =  defaultRespond( headers, session, uri ,rootDirs,wkHtmlTool);
        } catch ( Exception e) {
            WkLog.showLog("respond 发生异常，"+ e.getMessage());
            e.printStackTrace();
        }
        return r;
    }

    public NanoHTTPD.Response defaultRespond( Map<String, String> headers, NanoHTTPD.IHTTPSession session,
                                             String uri ,List<File> rootDirs ,WkHtmlTool wkHtmlTool)   {

        uri = uri.trim().replace(File.separatorChar, '/');
        if (uri.indexOf('?') >= 0) {
            uri = uri.substring(0, uri.indexOf('?'));
        }

        boolean canServeUri = false;
        File homeDir = null;
        for (int i = 0; !canServeUri && i <  rootDirs.size(); i++) {
            homeDir =  rootDirs.get(i);
            canServeUri = MyBaseFileTool.fileExists(uri, homeDir);
        }

        if (!canServeUri) {
            return render404();
        }


        File file = new File(homeDir, uri);
        NanoHTTPD.Response response ;

        if (file.isDirectory()) {
            if (file.canRead()) {
                response= addRangesHeaders(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_HTML, wkHtmlTool.listDirectory(   uri, file));
            } else {
                response= render403("文件夹没有读取权限。");
            }
        }else{
            // 获取文件类型 ， application/octet-stream  、image/png
            String mimeTypeForFile = NanoHTTPD.getMimeTypeForFile(uri);
            response = serveFile( headers, file, mimeTypeForFile);
        }
        return response != null ? response : render404();
    }

    private NanoHTTPD.Response serveFile(Map<String, String> header, File file, String mime) {
        NanoHTTPD.Response res;
        try {
            String etag = Integer.toHexString((file.getAbsolutePath() + file.lastModified() + "" + file.length()).hashCode());

            // Support (simple) skipping:
            long startFrom = 0;
            long endAt = -1;
            String range = header.get("range");
            if (range != null) {
                if (range.startsWith("bytes=")) {
                    range = range.substring("bytes=".length());
                    int minus = range.indexOf('-');
                    try {
                        if (minus > 0) {
                            startFrom = Long.parseLong(range.substring(0, minus));
                            endAt = Long.parseLong(range.substring(minus + 1));
                        }
                    } catch (NumberFormatException ignored) {
                    }
                }
            }

            // get if-range header. If present, it must match etag or else we
            // should ignore the range request
            String ifRange = header.get("if-range");
            boolean headerIfRangeMissingOrMatching = (ifRange == null || etag.equals(ifRange));

            String ifNoneMatch = header.get("if-none-match");
            boolean headerIfNoneMatchPresentAndMatching = ifNoneMatch != null
                    && ("*".equals(ifNoneMatch) || ifNoneMatch.equals(etag));

            // Change return code and add Content-Range header when skipping is
            // requested
            long fileLen = file.length();

            if (headerIfRangeMissingOrMatching && range != null && startFrom >= 0 && startFrom < fileLen) {
                // range request that matches current etag
                // and the startFrom of the range is satisfiable
                if (headerIfNoneMatchPresentAndMatching) {
                    // range request that matches current etag
                    // and the startFrom of the range is satisfiable
                    // would return range from file
                    // respond with not-modified
                    res = addRangesHeaders(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, "");
                    res.addHeader("ETag", etag);
                } else {
                    if (endAt < 0) {
                        endAt = fileLen - 1;
                    }
                    long newLen = endAt - startFrom + 1;
                    if (newLen < 0) {
                        newLen = 0;
                    }

                    FileInputStream fis = new FileInputStream(file);
                    fis.skip(startFrom);

                    res = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.PARTIAL_CONTENT, mime, fis, newLen);

                    res.addHeader("Accept-Ranges", "bytes");
                    res.addHeader("Content-Length", "" + newLen);
                    res.addHeader("Content-Range", "bytes " + startFrom + "-" + endAt + "/" + fileLen);
                    res.addHeader("ETag", etag);
                }
            } else {

                if (headerIfRangeMissingOrMatching && range != null && startFrom >= fileLen) {
                    // return the size of the file
                    // 4xx responses are not trumped by if-none-match
                    res = addRangesHeaders(NanoHTTPD.Response.Status.RANGE_NOT_SATISFIABLE, NanoHTTPD.MIME_PLAINTEXT, "");
                    res.addHeader("Content-Range", "bytes */" + fileLen);
                    res.addHeader("ETag", etag);
                } else if (range == null && headerIfNoneMatchPresentAndMatching) {
                    // full-file-fetch request
                    // would return entire file
                    // respond with not-modified
                    res = addRangesHeaders(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, "");
                    res.addHeader("ETag", etag);
                } else if (!headerIfRangeMissingOrMatching && headerIfNoneMatchPresentAndMatching) {
                    // range request that doesn't match current etag
                    // would return entire (different) file
                    // respond with not-modified

                    res = addRangesHeaders(NanoHTTPD.Response.Status.NOT_MODIFIED, mime, "");
                    res.addHeader("ETag", etag);
                } else {
                    // supply the file
                    res = addRangesHeaders(file, mime);
                    res.addHeader("Content-Length", "" + fileLen);
                    res.addHeader("ETag", etag);
                }
            }
        } catch (IOException ioe) {
            res = render403("Reading file failed.");
        }
        return res;
    }

    public   NanoHTTPD.Response addRangesHeaders(NanoHTTPD.Response.IStatus status, String mimeType, String message) {
        NanoHTTPD.Response response = NanoHTTPD.newFixedLengthResponse(status, mimeType, message);
        response.addHeader("Accept-Ranges", "bytes");
        return response;
    }

    public  NanoHTTPD.Response addRangesHeaders(File file, String mime) throws FileNotFoundException {
        NanoHTTPD.Response res  = NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, mime, new FileInputStream(file), (int) file.length());
        res.addHeader("Accept-Ranges", "bytes");
    return res;
}

    public NanoHTTPD.Response render403(String s) {
        return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "403!.........403!.........禁止访问。"+s);
        //return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: " + s);
    }

    public NanoHTTPD.Response render500(String s) {
        return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "500!.........500!......... ."+s);
        //return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.FORBIDDEN, NanoHTTPD.MIME_PLAINTEXT, "FORBIDDEN: " + s);
    }


    public NanoHTTPD.Response render404() {
        return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, "404!.........404!.........");
        //return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Error 404, file not found.");
    }



    public NanoHTTPD.Response renderApiMsg(String msg) {
        return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, NanoHTTPD.MIME_PLAINTEXT, msg);
    }


}
